package com.server.tools;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

/**
 * httpclient 请求工具类 
 * httpclient 连接池
 * @author weisibin
 * @date 2020-5-13 09:39:57
 */
public class HttpClientUtil {
	/**
	 * http链接池
	 */
	private static PoolingHttpClientConnectionManager cm = null;
	/**
	 * http客户端
	 */
	private static CloseableHttpClient httpClient = null;
	/**
	 * from-请求/响应   类型
	 */
	public static final String CONTENT_TYPE_FORM = "application/x-www-form-urlencoded;charset=UTF-8";
	/**
	 * json-请求/响应   类型
	 */
	public static final String CONTENT_TYPE_JSON = "application/json;charset=UTF-8";
	
	/**
	 * 长连接时间保持设置
	 */
	private static final int HTTP_DEFAULT_KEEP_TIME = 60000;
	
	/**
	 * 1、MaxtTotal是整个池子的大小； 2、DefaultMaxPerRoute是根据连接到的主机对MaxTotal的一个细分；比如：
	 * MaxtTotal=400 DefaultMaxPerRoute=200
	 * 而我只连接到http://sishuok.com时，到这个主机的并发最多只有200；而不是400； 而我连接到http://sishuok.com 和
	 * http://qq.com时，到每个主机的并发最多只有200；即加起来是400（但不能超过400）；
	 * 所以起作用的设置是DefaultMaxPerRoute。
	 */
	private static final int DEFAULT_MAX_PER_ROUTE = 1000;
	/**
	 * 设置连接池的大小
	 */
	private static final int MAX_TOTAL = 1000;

	/**
	 * 设置链接超时
	 */
	private static final int CONNECTION_TIMEOUT = 5000;
	/**
	 * 设置等待数据超时时间
	 */
	private static final int SOCKET_TIMEOUT = 15000;
	/**
	 * 初始化连接池
	 */
	private static synchronized void initPools() {
		if (httpClient == null) {
			cm = new PoolingHttpClientConnectionManager();
			cm.setDefaultMaxPerRoute(DEFAULT_MAX_PER_ROUTE);
			cm.setMaxTotal(MAX_TOTAL);
			httpClient = HttpClients.custom().setKeepAliveStrategy(defaultStrategy).setConnectionManager(cm).build();
		}
	}
	/**
	 * 长连接保持设置 Http connection keepAlive 设置
	 */
	private static ConnectionKeepAliveStrategy defaultStrategy = new ConnectionKeepAliveStrategy() {
		public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
			HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
			int keepTime = HTTP_DEFAULT_KEEP_TIME;
			while (it.hasNext()) {
				HeaderElement he = it.nextElement();
				String param = he.getName();
				String value = he.getValue();
				if (value != null && param.equalsIgnoreCase("timeout")) {
					try {
						return Long.parseLong(value) * 1000;
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
			return keepTime * 1000;
		}
	};

	/**
	 * 执行http post
	 * 请求 默认采用Content-Type：application/json
	 * 响应 默认采用Content-Type：application/json
	 * @param uri  请求地址
	 * @param data 请求数据
	 * @return String 返回是数据
	 * @throws Exception 请求异常
	 */
	public static String sendPost(String uri, String data) throws Exception {
		return sendPost(uri,data,CONTENT_TYPE_JSON,CONTENT_TYPE_JSON);
	}

	/**
	 * 执行http post请求
	 * @param uri 请求地址
	 * @param data 请求数据
	 * @param reqContentType 请求content-type
	 * @param respContentType 响应content-type
	 * @return String 返回请求结果
	 * @throws Exception 异常
	 */
	public static String sendPost(String uri, String data, String reqContentType, String respContentType)
			throws Exception {
		@SuppressWarnings("unused")
		long startTime = System.currentTimeMillis();
		HttpEntity httpEntity = null;
		HttpEntityEnclosingRequestBase method = null;
		String responseBody = "";
		try {
			if (httpClient == null) {
				initPools();
			}
			method = (HttpEntityEnclosingRequestBase) getRequest(uri, HttpPost.METHOD_NAME, reqContentType,
					respContentType);
			method.setEntity(new StringEntity(data, Charset.forName("UTF-8")));
			HttpContext context = HttpClientContext.create();
			CloseableHttpResponse httpResponse = httpClient.execute(method, context);
			httpEntity = httpResponse.getEntity();
			if (httpEntity != null) {
				responseBody = EntityUtils.toString(httpEntity, "UTF-8");
			}
		} catch (Exception e) {
			if (method != null) {
				method.abort();
			}
			e.printStackTrace();
			throw e;
		} finally {
			if (httpEntity != null) {
				try {
					EntityUtils.consumeQuietly(httpEntity);
				} catch (Exception e) {
					e.printStackTrace();
					throw e;
				}
			}
		}
		return responseBody;
	}
	
	/* 执行http post请求
	 * @param uri 请求地址
	 * @param data 请求数据
	 * @param reqContentType 请求content-type
	 * @param respContentType 响应content-type
	 * @return String 返回请求结果
	 * @throws Exception 异常
	 */
	public static String sendPost(String host, String path, Map<String, String> bodys, Map<String, String> querys)
			throws Exception {
		@SuppressWarnings("unused")
		long startTime = System.currentTimeMillis();
		HttpEntity httpEntity = null;
		HttpEntityEnclosingRequestBase method = null;
		String responseBody = "";
		try {
			if (httpClient == null) {
				initPools();
			}
			method = (HttpEntityEnclosingRequestBase) getRequest(buildUrl(host, path, querys), HttpPost.METHOD_NAME,CONTENT_TYPE_FORM,CONTENT_TYPE_JSON);
			method.setHeader("Authorization", "APPCODE 7656bf084d8b4889abb86ec88e44fc84");
			String body = "";
			for (String in : bodys.keySet()) {
				if(body != "") {
					body += "&" + in + "=" + bodys.get(in);
				}else {
					body += in + "=" + bodys.get(in);
				}
	        }
			
			method.setEntity(new StringEntity(body, Charset.forName("UTF-8")));
			HttpContext context = HttpClientContext.create();
			CloseableHttpResponse httpResponse = httpClient.execute(method, context);
			httpEntity = httpResponse.getEntity();
			if (httpEntity != null) {
				responseBody = EntityUtils.toString(httpEntity, "UTF-8");
			}
		} catch (Exception e) {
			if (method != null) {
				method.abort();
			}
			e.printStackTrace();
			throw e;
		} finally {
			if (httpEntity != null) {
				try {
					EntityUtils.consumeQuietly(httpEntity);
				} catch (Exception e) {
					e.printStackTrace();
					throw e;
				}
			}
		}
		return responseBody;
	}

	/**
	 * 执行GET 请求
	 * 请求 默认采用Content-Type：application/json
	 * 响应 默认采用Content-Type：application/json
	 * @param uri 请求链接
	 * @return String 请求结果
	 * @throws Exception 异常
	 */
	public static String sendGet(String uri) throws Exception {
		return sendGet(uri,CONTENT_TYPE_JSON,CONTENT_TYPE_JSON);
	}

	/**
	 * 执行GET 请求
	 * @param uri 请求链接
	 * @param reqContentType 请求ContentType
	 * @param respContentType 响应ContentType
	 * @return String 响应数据
	 * @throws Exception 异常
	 */
	public static String sendGet(String uri, String reqContentType, String respContentType) throws Exception {
		@SuppressWarnings("unused")
		long startTime = System.currentTimeMillis();
		HttpEntity httpEntity = null;
		HttpRequestBase method = null;
		String responseBody = "";
		try {
			if (httpClient == null) {
				initPools();
			}
			method = getRequest(uri, HttpGet.METHOD_NAME, reqContentType, respContentType);
			HttpContext context = HttpClientContext.create();
			CloseableHttpResponse httpResponse = httpClient.execute(method, context);
			httpEntity = httpResponse.getEntity();
			if (httpEntity != null) {
				responseBody = EntityUtils.toString(httpEntity, "UTF-8");
			}
		} catch (Exception e) {
			if (method != null) {
				method.abort();
			}
			e.printStackTrace();
			throw e;
		} finally {
			if (httpEntity != null) {
				try {
					EntityUtils.consumeQuietly(httpEntity);
				} catch (Exception e) {
					e.printStackTrace();
					throw e;
				}
			}
		}
		return responseBody;
	}

	/**
	 * 创建请求
	 * @param uri 请求url
	 * @param methodName 请求的方法类型
	 * @param reqContentType 请求ContentType
	 * @param respContentType 响应ContentType
	 * @return HttpRequestBase http请求的基本实现对象
	 */ 
	private static HttpRequestBase getRequest(String uri, String methodName, String reqContentType,
			String respContentType) {
		HttpRequestBase method = null;

		RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(SOCKET_TIMEOUT)
				.setConnectTimeout(CONNECTION_TIMEOUT).setConnectionRequestTimeout(CONNECTION_TIMEOUT)
				.setExpectContinueEnabled(false).build();
		if (HttpPut.METHOD_NAME.equalsIgnoreCase(methodName)) {
			method = new HttpPut(uri);
		} else if (HttpPost.METHOD_NAME.equalsIgnoreCase(methodName)) {
			method = new HttpPost(uri);
		} else if (HttpGet.METHOD_NAME.equalsIgnoreCase(methodName)) {
			method = new HttpGet(uri);
		} else {
			method = new HttpPost(uri);
		}
		if (StringUtils.isBlank(reqContentType)) {
			reqContentType = CONTENT_TYPE_FORM;
		}
		if (StringUtils.isBlank(respContentType)) {
			respContentType = CONTENT_TYPE_JSON;
		}
		// 请求类型
		method.addHeader("Content-Type", reqContentType);
		method.addHeader("Accept", respContentType);
		method.setConfig(requestConfig);
		return method;
	}
	/**
	 * 获取 httpClient
	 * @return CloseableHttpClient
	 */
	public static CloseableHttpClient getHttpClient() {
		return httpClient;
	}
	
	/**
	 * 获取 httpClient连接池
	 * @return PoolingHttpClientConnectionManager
	 */
	public static PoolingHttpClientConnectionManager getHttpConnectionManager() {
		return cm;
	}
	
	private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
    	StringBuilder sbUrl = new StringBuilder();
    	sbUrl.append(host);
    	if (!StringUtils.isBlank(path)) {
    		sbUrl.append(path);
        }
    	if (null != querys) {
    		StringBuilder sbQuery = new StringBuilder();
        	for (Map.Entry<String, String> query : querys.entrySet()) {
        		if (0 < sbQuery.length()) {
        			sbQuery.append("&");
        		}
        		if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
        			sbQuery.append(query.getValue());
                }
        		if (!StringUtils.isBlank(query.getKey())) {
        			sbQuery.append(query.getKey());
        			if (!StringUtils.isBlank(query.getValue())) {
        				sbQuery.append("=");
        				sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
        			}        			
                }
        	}
        	if (0 < sbQuery.length()) {
        		sbUrl.append("?").append(sbQuery);
        	}
        }
    	
    	return sbUrl.toString();
    }
}
